﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Classes_and_Interfaces.Exceptions;
using Classes_and_Interfaces.SupportClasses;

namespace Classes_and_Interfaces
{
    public class Harmony
    {
        /////////////////////////////////////////////////////////////////////////////////////////////////////////
        // КОНСТАНТЫ ////////////////////////////////////////////////////////////////////////////////////////////
        /////////////////////////////////////////////////////////////////////////////////////////////////////////

        const int BASS = 0, TENOR = 1, ALT = 2, SOPRANO = 3;
        const int I = 1, II = 2, III = 3, IV = 4, V = 5, VI = 6, VII = 0;
        
        
        /////////////////////////////////////////////////////////////////////////////////////////////////////////
        // ПОЛЯ /////////////////////////////////////////////////////////////////////////////////////////////////
        /////////////////////////////////////////////////////////////////////////////////////////////////////////

        protected int basis; //ступень, на которой строится начальная форма гармония
        protected string function; //функция или обращение функции гармонии
        protected Note[] notes; //ноты аккорда гармонии
        protected string raspologenie; //расположение: "тесное"/"широкое"/"смешанное"
        protected int countOfVoices=4;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////
        // КОНСТРУКТОРЫ /////////////////////////////////////////////////////////////////////////////////////////
        /////////////////////////////////////////////////////////////////////////////////////////////////////////

        public Harmony(){
        }

        //конструктор по ивестной ноте 
        public Harmony(   Note ourNote,       //наша известная нота
                          int basis,          //основной тон гармонии(ступень)
                          string function,    //функция
                          string raspologenie //расположене  
                          //в будущем тут должны быть дополнительные параметры конструктора
                      )
        {            
            //проверка ourNote, если она в басовом голосе, на соответствие базису
            if (!isNoteInBassVoiceRight(ourNote)){
                throw new NotRightHarmonyException();
            }
            
            this.basis = basis;
            this.function = function;
            this.raspologenie = raspologenie;
            
            if (function=="6")
                this.notes=StupensForHarmonyMaker.make6(PossibleHarmonyStupensForNoteMaker.make(ourNote, basis, function), ourNote, raspologenie, countOfVoices);
            else
                this.notes = StupensForHarmonyMaker.make(PossibleHarmonyStupensForNoteMaker.make(ourNote, basis, function), ourNote, raspologenie, countOfVoices);
        }
        
        /////////////////////////////////////////////////////////////////////////////////////////////////////////
        // МЕТОДЫ ///////////////////////////////////////////////////////////////////////////////////////////////
        /////////////////////////////////////////////////////////////////////////////////////////////////////////

        //проверки басового голоса
        private bool isNoteInBassVoiceRight(Note ourNote)
        {
            if (ourNote.getVoice() == BASS)
            {                
                if (
                      //АКСИОМА: в трезвучии и септаккорде в басу всегда базисный тон, проверяем это
                      (((this.function == "53") || (this.function == "7")) && (ourNote.getStupen() != basis))
                   ||
                      //АКСИОМА: в секстаккорде и квинтсекст аккорде в басу всегда терцовый тон, проверяем это
                      (((this.function == "6") || (this.function == "65")) && (ourNote.getStupen() != (basis + 2) % 7))
                   ||
                      //АКСИОМА: в квартсекстаккорде и терцквартаккорде в басу всегда квинтовый тон, проверяем это
                      (((this.function == "64") || (this.function == "43")) && (ourNote.getStupen() != (basis + 4) % 7))
                   ||
                      //АКСИОМА: в секундаккордах в басу всегда септимовый тон, проверяем это
                      ((this.function == "2") && (ourNote.getStupen() != (basis + 6) % 7))
                   )
                {
                    return false;
                }                
            }
            return true;
        }

        //функция вывода аккорда в консоль понотно в формате "номер голоса - номер ступени"  
        //p.s. красивая и медленная 
        public void showHarmony()
        {
            Console.Write("Basis: ");
            switch (this.basis)
            {
                case 1: Console.Write("I");   break;
                case 2: Console.Write("II");  break;
                case 3: Console.Write("III"); break;
                case 4: Console.Write("IV");  break;
                case 5: Console.Write("V");   break;
                case 6: Console.Write("VI");  break;
                case 0: Console.Write("VII"); break;
            }            
            Console.Write(";  Function: "); Console.WriteLine("'"+this.function+"'"+";");
            for (int i = ((this.notes.Length) - 1); i >= 0; i--)
            {
                Console.Write("    ");
                switch (i)
                {
                    case 3: Console.Write("soprano "); break;
                    case 2: Console.Write("    alt "); break;
                    case 1: Console.Write("  tenor "); break;
                    case 0: Console.Write("   bass "); break;                            
                }
                switch (notes[i].getStupen())
                {
                    case 1: Console.WriteLine("I");   break;
                    case 2: Console.WriteLine("II");  break;
                    case 3: Console.WriteLine("III"); break;
                    case 4: Console.WriteLine("IV");  break;
                    case 5: Console.WriteLine("V");   break;
                    case 6: Console.WriteLine("VI");  break;
                    case 0: Console.WriteLine("VII"); break;
                }
                
            }
        }

        public void showPossibleStupens(List<int> pS)
        {
            Console.WriteLine("Возможные ступени:");
            for (int i = 0; i < pS.Count; i++)
            {
                Console.WriteLine("        "+pS.ElementAt(i));
            }
        }
    }
}
